<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><title>每日一面--Spring入门 | Celts</title><meta name="author" content="PaulGeorge"><meta name="copyright" content="PaulGeorge"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="每天一篇Java小知识  本篇着重介绍一下 Spring 框架          写在前面 百度百科： Spring 框架是由于软件开发的复杂性而创建的。 Spring 使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而，Spring 的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言，绝大部分 Java 应用都可以从 Spring 中受益。">
<meta property="og:type" content="article">
<meta property="og:title" content="每日一面--Spring入门">
<meta property="og:url" content="https://curry3035.gitee.io/posts/16824/index.html">
<meta property="og:site_name" content="Celts">
<meta property="og:description" content="每天一篇Java小知识  本篇着重介绍一下 Spring 框架          写在前面 百度百科： Spring 框架是由于软件开发的复杂性而创建的。 Spring 使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而，Spring 的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言，绝大部分 Java 应用都可以从 Spring 中受益。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://curry3035.gitee.io/img/avatar.jpg">
<meta property="article:published_time" content="2021-06-19T13:58:58.000Z">
<meta property="article:modified_time" content="2022-05-26T18:17:45.641Z">
<meta property="article:author" content="PaulGeorge">
<meta property="article:tag" content="Spring">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://curry3035.gitee.io/img/avatar.jpg"><link rel="shortcut icon" href="/img/ic.ico"><link rel="canonical" href="https://curry3035.gitee.io/posts/16824/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":false,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: true,
    post: true
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: {"limitCount":50,"languages":{"author":"作者: PaulGeorge","link":"链接: ","source":"来源: Celts","info":"著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"}},
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"top-right"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '每日一面--Spring入门',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-05-27 02:17:45'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/css/background.css"><link rel="stylesheet" href="/css/my.css"><meta name="generator" content="Hexo 5.4.2"></head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/avatar.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">97</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">64</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">25</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 图库</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@img/mig2023/background05.jpg')"><nav id="nav"><span id="blog-info"><a href="/" title="Celts"><span class="site-name">Celts</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 图库</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">每日一面--Spring入门</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-19T13:58:58.000Z" title="发表于 2021-06-19 21:58:58">2021-06-19</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-05-26T18:17:45.641Z" title="更新于 2022-05-27 02:17:45">2022-05-27</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E6%A1%86%E6%9E%B6/">框架</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">5.8k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>21分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="每日一面--Spring入门"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><blockquote>
<p>每天一篇Java小知识</p>
</blockquote>
<p><strong>本篇着重介绍一下 Spring 框架</strong></p>
<hr>
<br>

<img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-06-20_17-58-03.png" style="zoom:50%;" />

<br>

<br>

<p><strong>写在前面</strong></p>
<p>百度百科：</p>
<p>Spring 框架是由于软件开发的复杂性而创建的。</p>
<p>Spring 使用的是基本的<code>JavaBean</code>来完成以前只可能由<code>EJB</code>完成的事情。然而，Spring 的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言，绝大部分 Java 应用都可以从 Spring 中受益。</p>
<span id="more"></span>



<h2 id="Spring"><a href="#Spring" class="headerlink" title="Spring"></a>Spring</h2><h3 id="Spring-核心"><a href="#Spring-核心" class="headerlink" title="Spring 核心"></a>Spring 核心</h3><h4 id="Spring-的整体架构"><a href="#Spring-的整体架构" class="headerlink" title="Spring 的整体架构"></a>Spring 的整体架构</h4><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/1555451-20200715233519469-2015600342.png" style="zoom:90%;" />

<hr>
<h4 id="Spring-应用的设计模式"><a href="#Spring-应用的设计模式" class="headerlink" title="Spring 应用的设计模式"></a>Spring 应用的设计模式</h4><ul>
<li><strong>工厂设计模式</strong> : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。</li>
<li><strong>代理设计模式</strong> : Spring AOP 功能的实现。</li>
<li><strong>单例设计模式</strong> : Spring 中的 Bean 默认都是单例的。</li>
<li><strong>模板方法模式</strong> : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类，它们就使用到了模板模式。</li>
<li><strong>包装器设计模式</strong> : 我们的项目需要连接多个数据库，而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。</li>
<li><strong>观察者模式</strong>: Spring 事件驱动模型就是观察者模式很经典的一个应用。</li>
<li><strong>适配器模式</strong> :Spring AOP 的增强或通知 (Advice) 使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配 controller。</li>
</ul>
<h3 id="Spring-三层架构"><a href="#Spring-三层架构" class="headerlink" title="Spring 三层架构"></a>Spring 三层架构</h3><ul>
<li>A 表现层  <code>web</code>层 、 <code>MVC</code>是表现层的一个设计模型 </li>
<li>B 业务层 <code>service</code>层</li>
<li>C 持久层 <code>dao</code>层</li>
</ul>
<h5 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h5><p>要与 SPringMVC 的三层架构区分开。</p>
<h4 id="核心容器"><a href="#核心容器" class="headerlink" title="核心容器"></a>核心容器</h4><ul>
<li><strong>spring-core</strong> 模块提供了框架的基本组成部分，包括 <code>IoC</code> 和依赖注入功能。</li>
<li><strong>spring-beans</strong> 模块提供 BeanFactory，工厂模式的微妙实现，它移除了编码式单例的需要，并且可以把配置和依赖从实际编码逻辑中解耦。</li>
<li><strong>context</strong> 模块建立在由 <strong>core</strong>和 <strong>beans</strong> 模块的基础上建立起来的，它以一种类似于 JNDI 注册的方式访问对象。Context 模块继承自 Bean 模块，并且添加了国际化（比如，使用资源束）、事件传播、资源加载和透明地创建上下文（比如，通过 Servelet 容器）等功能。Context 模块也支持 Java EE 的功能，比如 EJB、JMX 和远程调用等。<strong>ApplicationContext</strong> 接口是 Context 模块的焦点。<strong>spring-context-support</strong> 提供了对第三方集成到 Spring 上下文的支持，比如缓存（EhCache, Guava, JCache）、邮件（JavaMail）、调度（CommonJ, Quartz）、模板引擎（FreeMarker, JasperReports, Velocity）等。</li>
<li><strong>spring-expression</strong> 模块提供了强大的表达式语言，用于在运行时查询和操作对象图。它是 JSP2.1 规范中定义的统一表达式语言的扩展，支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从 Spring IoC 容器检索对象，还支持列表的投影、选择以及聚合等。</li>
</ul>
<h4 id="数据访问-集成"><a href="#数据访问-集成" class="headerlink" title="数据访问/集成"></a>数据访问/集成</h4><p><font color=#FF0000>注：JDBC【Java Data Base Connectivity】，ORM【Object Relational Mapping】，OXM【Object XML Mapping】，JMS【Java Message Service】</font></p>
<ul>
<li><strong>JDBC</strong> 模块提供了 JDBC 抽象层，它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。</li>
<li><strong>ORM</strong> 模块提供了对流行的对象关系映射 API 的集成，包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring的其它功能整合，比如前面提及的事务管理。</li>
<li><strong>OXM</strong> 模块提供了对 OXM 实现的支持，比如 JAXB、Castor、XML Beans、JiBX、XStream 等。</li>
<li><strong>JMS</strong> 模块包含生产（produce）和消费（consume）消息的功能。从 Spring 4.1 开始，集成了 spring-messaging 模块。</li>
<li><strong>事务</strong>模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。（注：编程式事务需要自己写 beginTransaction()、commit()、rollback() 等事务管理方法，声明式事务是通过注解或配置由 spring 自动处理，编程式事务粒度更细）</li>
</ul>
<h4 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h4><p>Web 层由 Web，Web-MVC，Web-Socket 和 Web-Portlet 组成，它们的细节如下：</p>
<ul>
<li><strong>Web</strong> 模块提供面向 web 的基本功能和面向 web 的应用上下文，比如多部分（multipart）文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。</li>
<li><strong>Web-MVC</strong> 模块为 web 应用提供了模型视图控制（MVC）和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离，且可以与 Spring 框架的其它所有功能进行集成。</li>
<li><strong>Web-Socket</strong> 模块为 WebSocket-based 提供了支持，而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。</li>
<li><strong>Web-Portlet</strong> 模块提供了用于 Portlet 环境的 MVC 实现，并反映了 spring-webmvc 模块的功能。</li>
</ul>
<hr>
<h3 id="Spring-框架的优点"><a href="#Spring-框架的优点" class="headerlink" title="Spring 框架的优点"></a>Spring 框架的优点</h3><ol>
<li>非侵入式设计</li>
<li>方便解耦、简化开发</li>
<li>支持AOP</li>
<li>支持声明式事务处理</li>
<li>方便程序的测试</li>
<li>方便集成各种优秀框架</li>
<li>降低Jave EE API的使用难度。</li>
</ol>
<p><font color=#008000>说到 spring 的容器管理就不得不先提一下 bean 的概念</font></p>
<h3 id="Bean-的概念"><a href="#Bean-的概念" class="headerlink" title="Bean 的概念"></a>Bean 的概念</h3><ol>
<li>在 Java 中，“Bean”是对“可重用组件”的惯用叫法。组件本身就是一个抽象概念，Bean 作为其代称，也是一个抽象概念，当我们将一个类或一个对象作为组件来考虑时，就可以称它为 Bean。</li>
<li>在 Spring 中，Bean 的概念同上，它有时也被称为 Component。由 Spring 容器管理的 Bean 则称为 Spring Bean。</li>
</ol>
<p><strong>扩展：</strong></p>
<ul>
<li><p><em>Java Bean</em> 的概念不同于 Bean，Java Bean 是指符合 <em>JavaBeans</em> 规范的一类特殊的 Bean，即：所有属性均为 private，提供 getter 和 setter，提供默认构造方法。JavaBean 也可以认为是遵循特定约定的 POJO。</p>
</li>
<li><p>POJO（Plain Ordinary Java Object）是指简单且普通的 Java 对象。严格来说，它不继承类，不实现接口，不处理业务逻辑，仅用于封装数据。</p>
</li>
</ul>
<p><font color=#008000>说到 spring 就不得不提到两个重要的概念</font></p>
<ul>
<li>IoC 【控制反转】</li>
<li>DI 【依赖注入】</li>
</ul>
<h3 id="IOC-与-DI"><a href="#IOC-与-DI" class="headerlink" title="IOC 与 DI"></a>IOC 与 DI</h3><h4 id="IOC-定义"><a href="#IOC-定义" class="headerlink" title="IOC 定义"></a>IOC 定义</h4><p>IoC（Inverse Of Control，控制反转）是一种设计思想，所谓控制反转，指的是对资源的控制方式进行反转。</p>
<p><strong>IOC 容器</strong>具有依赖注入功能的容器，它可以创建对象，IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。</p>
<h4 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h4><p>【控制权在自己】：在 OOP 中，对象之间往往存在某种依赖关系，当一个对象依赖另一个对象时，传统 OOP 的做法是在它内部直接 <strong>new</strong> 一个出来，这种做法是由对象自己主动创建并管理依赖资源。</p>
<p>【控制权在 Spring 容器】：”控制反转”是指 <strong>new</strong> 实例工作不由程序员来做而是交给 Spring 容器来做。使得对象不再主动控制依赖资源，而是被动接受资源，IoC 要求将资源的控制权下放给 <strong>Ioc</strong> 容器，它将对资源进行集中管理，对象需要什么资源就从容器中取，或者让容器主动将资源注入进来。</p>
<h4 id="好处"><a href="#好处" class="headerlink" title="好处"></a>好处</h4><p>在 IoC 之后，对象与依赖资源之间不再具有强耦合性，资源可以被直接替换，而无需改动需求方的代码。</p>
<h4 id="注意-1"><a href="#注意-1" class="headerlink" title="注意"></a>注意</h4><p>在 Spring 中 <strong>BeanFactory</strong> 是 IOC 容器的实际代表者。【工厂模式】</p>
<p><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-06-20_20-38-32.png"></p>
<hr>
<h4 id="Spring-的两种-IoC-容器"><a href="#Spring-的两种-IoC-容器" class="headerlink" title="Spring 的两种 IoC 容器"></a>Spring 的两种 IoC 容器</h4><p>Spring 提供了两种 IoC 容器： BeanFactory 和 ApplicationContext </p>
<ul>
<li>BeanFactory 提供基本的 IoC 服务支持。</li>
<li>ApplicationContext 对 BeanFactory 进行了扩展与增强，除了拥有 BeanFactory 的所有能力外，还提供了许多高级特性，如事件发布、资源加载、国际化消息等。ApplicationContext 接口继承自 BeanFactory 接口，它的实现也是直接复用了 BeanFactory 的实现，因此可以说，ApplicationContext 是 BeanFactory 的增强版。</li>
</ul>
<p>两者在核心功能上的区别主要是默认的加载策略不同，这点区别几乎可以忽略不计，通常情况下，我们总是使用更为强大的 ApplicationContext，很少会直接使用 BeanFactory。</p>
<h4 id="IoC-的两种实现方式"><a href="#IoC-的两种实现方式" class="headerlink" title="IoC 的两种实现方式"></a>IoC 的两种实现方式</h4><ul>
<li>DI（Dependency Injection，依赖注入）<br>所谓依赖注入，是指对象所依赖的资源将通过被动注入的方式得到，换言之，容器会主动地根据预先配置的依赖关系将资源注入进来。</li>
<li>DL（Dependency Lookup，依赖查找）<br>依赖查找是早先提出的一种 IoC 实现方式，它要求对象主动查找依赖资源，这种方式已经不再使用。</li>
</ul>
<h4 id="DI-定义"><a href="#DI-定义" class="headerlink" title="DI 定义"></a>DI 定义</h4><p>Spring 容器在创建被调用者的实例时，会自动将调用者需要的对象实例注入给调用者，这样，调用者通过 Spring 容器获得被调用者实例，这称为依赖注入。</p>
<h4 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h4><p>依赖注入主要有两种实现方式，分别是属性 setter 注入和构造方法注入</p>
<ol>
<li><strong>属性 setter 注入</strong></li>
</ol>
<p>指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后，调用该 bean 的 setter 方法，即可实现基于 setter 的 DI。</p>
<ol start="2">
<li><strong>构造方法注入</strong></li>
</ol>
<p>指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现，每个参数代表一个依赖。</p>
<h4 id="注意-2"><a href="#注意-2" class="headerlink" title="注意"></a>注意</h4><p>依赖注入（Dependency Injection，DI）和控制反转含义相同，它们是从两个角度描述的同一个概念。</p>
<hr>
<h3 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h3><h4 id="AOP-的定义"><a href="#AOP-的定义" class="headerlink" title="AOP 的定义"></a>AOP 的定义</h4><ol>
<li><p>AOP（面向切面编程），它是利用对业务逻辑的各个部分进行隔离，从而使得业务逻辑各部分之间的耦合度降低，提高程序的可重用性，同时提高了开发的效率。</p>
</li>
<li><p>AOP 采取横向抽取机制，取代了传统纵向继承体系重复性代码（性能监视、事务管理、安全检查、缓存）</p>
</li>
</ol>
<p><strong>AOP：面向切面编程【底层就是动态代理】，指程序在运行期间动态的将某段代码切入到指定方法位置进行的编程方式。</strong></p>
<h4 id="AOP-的优势"><a href="#AOP-的优势" class="headerlink" title="AOP 的优势"></a>AOP 的优势</h4><ol>
<li>减少重复的代码</li>
<li>提供开发的效率</li>
<li>维护方便</li>
</ol>
<h4 id="AOP-体系"><a href="#AOP-体系" class="headerlink" title="AOP 体系"></a>AOP 体系</h4><p><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-07-24_20-13-10.png"></p>
<h4 id="AOP-的使用场景"><a href="#AOP-的使用场景" class="headerlink" title="AOP 的使用场景"></a>AOP 的使用场景</h4><p><strong>日志场景</strong></p>
<ul>
<li>诊断上下文，如：【log4j 或 logback 】</li>
<li>辅助信息，如：方法执行时间 【System.currentTimeMillis()】</li>
</ul>
<p><strong>统计场景</strong></p>
<ul>
<li>方法调用次数</li>
<li>执行异常次数</li>
<li>数据抽样</li>
<li>数值累加</li>
</ul>
<p><strong>安防场景</strong></p>
<ul>
<li>熔断，如：【Netflix Hystrix】</li>
<li>限流和降级：如：【Alibaba Sentinel】</li>
<li>认证和授权，如：【Spring Security】</li>
<li>监控，如：【JMX】</li>
</ul>
<p><strong>性能场景</strong></p>
<ul>
<li><p>缓存，如 【Spring Cache】</p>
</li>
<li><p>超时控制</p>
</li>
</ul>
<h4 id="AOP-的底层原理"><a href="#AOP-的底层原理" class="headerlink" title="AOP 的底层原理"></a>AOP 的底层原理</h4><p><strong>JDK的动态代理技术（主要）</strong></p>
<ol>
<li>为接口创建代理类的字节码文件</li>
<li>使用 ClassLoader 将字节码文件加载到 JVM</li>
<li>创建代理类实例对象，执行对象的目标方法</li>
</ol>
<h4 id="AOP-的重要名词"><a href="#AOP-的重要名词" class="headerlink" title="AOP 的重要名词"></a>AOP 的重要名词</h4><ol>
<li><p><strong>【 Joinpoint 】连接点</strong> </p>
<p>所谓连接点是指那些被拦截到的点。在spring中指的是所有方法,因为spring只支持方法类型的连接点。</p>
</li>
<li><p><strong>【 Pointcut 】切入点</strong></p>
<p> （编写切入点表达式），程序增强的入口。</p>
</li>
<li><p><strong>【 Advice 】通知/增强</strong></p>
<p>对spring中方法要进行增强，编写事务管理相关代码。</p>
</li>
<li><p><strong>【 Target 】目标对象</strong></p>
<p>代理的目标对象</p>
</li>
<li><p><strong>【 Weaving 】织入</strong></p>
<p>是指把增强应用到目标对象来创建新的代理对象的过程</p>
</li>
<li><p><strong>【 Proxy 】代理</strong></p>
<p>一个类被AOP织入增强后，就产生一个结果代理类</p>
</li>
<li><p><font color=#FF8C00>Aspect(切面) = 切入点 + 通知</font></p>
</li>
</ol>
<h4 id="AOP-通知执行顺序"><a href="#AOP-通知执行顺序" class="headerlink" title="AOP 通知执行顺序"></a>AOP 通知执行顺序</h4><h5 id="执行顺序"><a href="#执行顺序" class="headerlink" title="执行顺序"></a>执行顺序</h5><p><strong>正常情况</strong></p>
<p><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-07-25_01-29-28.png"></p>
<p><strong>异常情况</strong></p>
<p><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-07-25_01-29-42.png"></p>
<p><strong>多个切面的情况（假设正常情况）</strong></p>
<p><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-07-25_01-29-12.png"></p>
<h5 id="注意-3"><a href="#注意-3" class="headerlink" title="注意"></a>注意</h5><p>从 Spring 5.2.7 开始，Spring AOP 不再严格按照 AspectJ 定义的规则来执行 advice，而是根据其类型按照从高到低的优先级进行执行：@Around，@Before ，@After，@AfterReturning，@AfterThrowing</p>
<h5 id="1、单个切面类"><a href="#1、单个切面类" class="headerlink" title="1、单个切面类"></a>1、单个切面类</h5><p><strong>Spring 5.28：</strong><br>　　　　正常情况：around 环绕前置 ==&gt; @Before ==&gt; target 目标方法执行 ==&gt; @AfterReturning ==&gt; @After ==&gt; around 环绕返回 ==&gt; around 环绕最终<br>　　　　异常情况：around 环绕前置 ==&gt; @Before ==&gt; target 目标方法执行 ==&gt; @AfterThrowing ==&gt; @After ==&gt; around 环绕异常 ==&gt; around 环绕最终</p>
<h5 id="2、多个切面"><a href="#2、多个切面" class="headerlink" title="2、多个切面"></a>2、多个切面</h5><img src="https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@master/img/all/Snipaste_2021-07-25_01-05-02.png" style="zoom:80%;" />

<p><strong>Spring 5.28：</strong><br>　　　　正常情况：切面1环绕前置 ==&gt; 切面1@Before ==&gt; 切面2环绕前置 ==&gt; 切面2@Before ==&gt; 目标方法执行 ==&gt; 切面2@AfterReturning ==&gt; 切面2@After ==&gt; 切面2环绕返回 ==&gt; 切面2环绕最终 ==&gt; 切面1@AfterReturning ==&gt; 切面1@After ==&gt; 切面1环绕返回 ==&gt; 切面1环绕最终<br>　　　　异常情况：切面1环绕前置 ==&gt; 切面1@Before ==&gt; 切面2环绕前置 ==&gt; 切面2@Before ==&gt; 目标方法执行 ==&gt; 切面2@AfterThrowing ==&gt; 切面2@After ==&gt; 切面2环绕异常 ==&gt; 切面2环绕最终 ==&gt; 切面1@AfterThrowing ==&gt; 切面1@After ==&gt; 切面1环绕异常 ==&gt; 切面1环绕最终</p>
<h5 id="3、-Order注解指定先后顺序"><a href="#3、-Order注解指定先后顺序" class="headerlink" title="3、@Order注解指定先后顺序"></a>3、@Order注解指定先后顺序</h5><p>数字越小，优先级越高，先进后出</p>
<p>举个荔枝：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Order(value = 1)</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceProxy</span> &#123;&#125;</span><br><span class="line"> </span><br><span class="line"><span class="meta">@Order(value = 0)</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceProxy2</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 结果： </span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 切面二：环绕前置通知</span></span><br><span class="line"><span class="comment"> * 切面二：<span class="doctag">@Before</span></span></span><br><span class="line"><span class="comment"> * 切面一：环绕前置通知</span></span><br><span class="line"><span class="comment"> * 切面一：<span class="doctag">@Before</span></span></span><br><span class="line"><span class="comment"> * 目标方法执行</span></span><br><span class="line"><span class="comment"> * 切面一：<span class="doctag">@AfterReturning</span></span></span><br><span class="line"><span class="comment"> * 切面一：<span class="doctag">@After</span></span></span><br><span class="line"><span class="comment"> * 切面一：环绕返回通知</span></span><br><span class="line"><span class="comment"> * 切面一：环绕最终通知</span></span><br><span class="line"><span class="comment"> * 切面二：<span class="doctag">@AfterReturning</span></span></span><br><span class="line"><span class="comment"> * 切面二：<span class="doctag">@After</span></span></span><br><span class="line"><span class="comment"> * 切面二：环绕返回通知</span></span><br><span class="line"><span class="comment"> * 切面二：环绕最终通知</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure>

<p> <strong>由此得出：</strong></p>
<p>spring aop 就是一个同心圆，要执行的方法为圆心，最外层的 order 最小。</p>
<h4 id="AOP-实践"><a href="#AOP-实践" class="headerlink" title="AOP 实践"></a>AOP 实践</h4><p>AOP看起来很麻烦, 只要3步就可以了:</p>
<ol>
<li>将业务逻辑组件和切面类都加入到容器中, 告诉spring哪个是切面类(@Aspect)</li>
<li>在切面类上的每个通知方法上标注通知注解, 告诉Spring何时运行(写好切入点表达式,参照官方文档)</li>
<li>开启基于注解的AOP模式 @EableXXXX</li>
</ol>
<h5 id="1、引入-Aop-的依赖"><a href="#1、引入-Aop-的依赖" class="headerlink" title="1、引入 Aop 的依赖"></a>1、引入 Aop 的依赖</h5><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h5 id="2、编写-Aop-切面类"><a href="#2、编写-Aop-切面类" class="headerlink" title="2、编写 Aop 切面类"></a>2、编写 Aop 切面类</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Log4j2</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LogAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点</span></span><br><span class="line"><span class="comment">     * 切点的集合，这个表达式所描述的是一个虚拟面（规则）</span></span><br><span class="line"><span class="comment">     * 就是为了Annotation扫描时能够拿到注解中的内容</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * execution函数用于匹配方法执行的连接点，语法为：</span></span><br><span class="line"><span class="comment">     * execution(方法修饰符(可选)  返回类型  方法名  参数  异常模式(可选))</span></span><br><span class="line"><span class="comment">     * 参数部分允许使用通配符：</span></span><br><span class="line"><span class="comment">     * *  匹配任意字符，但只能匹配一个元素</span></span><br><span class="line"><span class="comment">     * .. 匹配任意字符，可以匹配任意多个元素，表示类时，必须和*联合使用</span></span><br><span class="line"><span class="comment">     * +  必须跟在类名后面，如Horseman+，表示类本身和继承或扩展指定类的所有类</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(public * com.ase.aop.controller.*.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aspect</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置环绕通知,使用在方法aspect()上注册的切入点</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Around(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">around</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            ((ProceedingJoinPoint) joinPoint).proceed();</span><br><span class="line">            <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">            log.info(<span class="string">&quot;around &quot;</span> + joinPoint + <span class="string">&quot;\tUse time : &quot;</span> + (end - start) + <span class="string">&quot; ms!&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">            log.info(<span class="string">&quot;around &quot;</span> + joinPoint + <span class="string">&quot;\tUse time : &quot;</span> + (end - start) + <span class="string">&quot; ms with exception : &quot;</span> + e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 前置通知: 在目标方法()运行之前运行 (<span class="doctag">@Before</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Before(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logStart</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;before: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 后置通知：在目标方法()运行结束之后运行,无论正常或异常结束 (<span class="doctag">@After</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@After(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logAfter</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;after: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回通知：在目标方法()正常返回之后运行 (<span class="doctag">@AfterReturning</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logAfterReturn</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;afterReturn: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 异常通知：在目标方法()出现异常后运行(<span class="doctag">@AfterThrowing</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AfterThrowing(pointcut = &quot;aspect()&quot;, throwing = &quot;ex&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logException</span><span class="params">(JoinPoint joinPoint, Exception ex)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;afterThrow: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">        log.info(<span class="string">&quot;message: &#123;&#125;&quot;</span>, ex.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="3、编写切点方法"><a href="#3、编写切点方法" class="headerlink" title="3、编写切点方法"></a>3、编写切点方法</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BaseController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/aop&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String, Object&gt; <span class="title function_">api1</span><span class="params">()</span> &#123;</span><br><span class="line">        Map&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">16</span>);</span><br><span class="line">        map.put(<span class="string">&quot;nowTime&quot;</span>, LocalDateTime.now().format(DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>)));</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="4、结果"><a href="#4、结果" class="headerlink" title="4、结果"></a>4、结果</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet <span class="string">&#x27;dispatcherServlet&#x27;</span></span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] o.s.web.servlet.DispatcherServlet        : Initializing Servlet <span class="string">&#x27;dispatcherServlet&#x27;</span></span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] o.s.web.servlet.DispatcherServlet        : Completed initialization in <span class="number">0</span> ms</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] com.ase.aop.config.LogAspect             : before: execution(Map com.ase.aop.controller.BaseController.api1())</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] com.ase.aop.controller.BaseController    : map = &#123;nowTime=<span class="number">2021</span>-<span class="number">07</span>-<span class="number">24</span> <span class="number">20</span>:<span class="number">37</span>:<span class="number">05</span>&#125;</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] com.ase.aop.config.LogAspect             : afterReturn: execution(Map com.ase.aop.controller.BaseController.api1())</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] com.ase.aop.config.LogAspect             : after: execution(Map com.ase.aop.controller.BaseController.api1())</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">2</span>] com.ase.aop.config.LogAspect             : around <span class="title function_">execution</span><span class="params">(Map com.ase.aop.controller.BaseController.api1()</span>)	Use time : <span class="number">16</span> ms!</span><br></pre></td></tr></table></figure>



<h5 id="注意-4"><a href="#注意-4" class="headerlink" title="注意"></a>注意</h5><p><strong>JoinPoint 对象封装了 Spring Aop 中切面方法的信息</strong>,在切面方法中添加 JoinPoint 参数,就可以获取到封装了该方法信息的JoinPoint对象.。</p>
<table>
<thead>
<tr>
<th>方法名</th>
<th>功能</th>
</tr>
</thead>
<tbody><tr>
<td>Signature getSignature()</td>
<td>获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息</td>
</tr>
<tr>
<td>Object[] getArgs()</td>
<td>获取传入目标方法的参数对象</td>
</tr>
<tr>
<td>Object getTarget()</td>
<td>获取被代理的对象</td>
</tr>
<tr>
<td>Object getThis()</td>
<td>获取代理对象</td>
</tr>
</tbody></table>
<h4 id="使用-annotation（注解）"><a href="#使用-annotation（注解）" class="headerlink" title="使用 annotation（注解）"></a>使用 annotation（注解）</h4><p>可以定义一个注解，注解分为两部分 :</p>
<p>① 元注解 ; ② public @interface 注解名称 ;</p>
<h5 id="annotation-注解属性"><a href="#annotation-注解属性" class="headerlink" title="annotation 注解属性"></a>annotation 注解属性</h5><p>注解的本质是接口 , 接口中可以定义 常量 和 方法 ;</p>
<p>在注解中定义 接口方法 , 就是 注解的属性 ;</p>
<p>为注解添加属性 : 接口中的方法都是抽象方法 , 其中 public abstract 可以省略 ;</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.METHOD)</span>            <span class="comment">//  注解用于字段上</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span>    <span class="comment">// 保留到运行时，可通过注解获取</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> MyAnnotation &#123;</span><br><span class="line">	<span class="comment">//public abstract String path();</span></span><br><span class="line">    String <span class="title function_">path</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注解属性使用格式 :</p>
<blockquote>
<p>@注解名称(属性名称 = 属性值)</p>
</blockquote>
<p>举个荔枝：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@MyAnnotation(path = &quot;&quot;)</span></span><br><span class="line">MyMethods(String arg)&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h5 id="annotation-注解属性类型"><a href="#annotation-注解属性类型" class="headerlink" title="annotation 注解属性类型"></a>annotation 注解属性类型</h5><p>注解属性 ( 接口方法 ) 返回值类型要求 :</p>
<p>① 基本数据类型 : byte , short , int , long , float , double , char , boolean ;</p>
<p>② 字符串类型 : String ;</p>
<p>③ 枚举类型 : enum ;</p>
<p>④ 注解类型 ;</p>
<p>⑤ 以上类型的数组形式 ;</p>
<p><strong>注解属性返回值必须是以上的类型 , 不能设置其它类型返回值 , 否则会报错 ;</strong></p>
<p>定义 注解属性 时 , 可以 使用 default 关键字 指定属性默认值 </p>
<p>举个荔枝：注解属性 intValue 值类型为 int 整型 , 默认值 100</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">intValue</span><span class="params">()</span> <span class="keyword">default</span> <span class="number">100</span>;</span><br></pre></td></tr></table></figure>

<p><strong>注意</strong></p>
<p>如果 注解属性指定了默认值 , 在使用注解时 , 可以选择不为该属性赋值 ( 此时使用默认属性值 ) , 也可以进行赋值 ( 指定一个新的属性值 ) ;</p>
<p>如果 注解属性没有指定默认值 , 则使用注解 时 , 必须为其指定一个默认值 , 否则编译时报错 ;</p>
<p>如果 注解属性名称是 value , 并且 注解中只有 1 个属性 , 那么在使用注解为注解属性赋值时 , 可以省略注解名称 , 直接传入注解属性值 ;</p>
<h5 id="annotation-反射获取注解"><a href="#annotation-反射获取注解" class="headerlink" title="annotation 反射获取注解"></a>annotation 反射获取注解</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Log4j2</span></span><br><span class="line"><span class="meta">@DisplayName(&quot;Annotation 自定义注解测试&quot;)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyAnnotationTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//使用我们的自定义注解</span></span><br><span class="line">    <span class="meta">@MyAnnotation(path = &quot;Annotation-Aop&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String path;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="meta">@DisplayName(&quot;自定义注解&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">myAnnotationTest</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取类模板</span></span><br><span class="line">        <span class="type">Class</span> <span class="variable">c</span> <span class="operator">=</span> MyAnnotationTest.class;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取所有字段</span></span><br><span class="line">        <span class="keyword">for</span> (Field field : c.getDeclaredFields()) &#123;</span><br><span class="line">            <span class="comment">// 判断这个字段是否有MyField注解</span></span><br><span class="line">            <span class="keyword">if</span> (field.isAnnotationPresent(MyAnnotation.class)) &#123;</span><br><span class="line">                <span class="type">MyAnnotation</span> <span class="variable">annotation</span> <span class="operator">=</span> field.getAnnotation(MyAnnotation.class);</span><br><span class="line">                log.info(<span class="string">&quot;字段:[&quot;</span> + field.getName() + <span class="string">&quot;], 描述:[&quot;</span> + annotation.path() + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">[main] INFO com.ase.aop.annotation.MyAnnotationTest - 字段:[path], 描述:[Annotation-Aop]</span><br></pre></td></tr></table></figure>



<h4 id="Log-日志实践"><a href="#Log-日志实践" class="headerlink" title="Log 日志实践"></a>Log 日志实践</h4><h5 id="1、引入-Aop-的依赖-1"><a href="#1、引入-Aop-的依赖-1" class="headerlink" title="1、引入 Aop 的依赖"></a>1、引入 Aop 的依赖</h5><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h5 id="2、自定义注解"><a href="#2、自定义注解" class="headerlink" title="2、自定义注解"></a>2、自定义注解</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.METHOD)</span>            <span class="comment">//  注解用于字段上</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span>    <span class="comment">// 保留到运行时，可通过注解获取</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> MyLog &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="3、编写-Aop-切面类"><a href="#3、编写-Aop-切面类" class="headerlink" title="3、编写 Aop 切面类"></a>3、编写 Aop 切面类</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Log4j2</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LogAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点</span></span><br><span class="line"><span class="comment">     * 切点的集合，这个表达式所描述的是一个虚拟面（规则）</span></span><br><span class="line"><span class="comment">     * 就是为了Annotation扫描时能够拿到注解中的内容</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;@annotation(com.ase.aop.annotation.MyLog)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aspect</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置环绕通知,使用在方法aspect()上注册的切入点</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Around(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">around</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">// 获取方法名称</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().getName();</span><br><span class="line">        <span class="comment">// 获取入参</span></span><br><span class="line">        Object[] param = joinPoint.getArgs();</span><br><span class="line"></span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="keyword">for</span>(Object o : param)&#123;</span><br><span class="line">            sb.append(o + <span class="string">&quot;; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;进入[&quot;</span> + methodName + <span class="string">&quot;]方法,参数为:&quot;</span> + sb.toString());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 继续执行方法</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            ((ProceedingJoinPoint) joinPoint).proceed();</span><br><span class="line">            <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">            log.info(<span class="string">&quot;around &quot;</span> + joinPoint + <span class="string">&quot;\tUse time : &quot;</span> + (end - start) + <span class="string">&quot; ms!&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable throwable) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">            log.info(<span class="string">&quot;around &quot;</span> + joinPoint + <span class="string">&quot;\tUse time : &quot;</span> + (end - start) + <span class="string">&quot; ms with exception : &quot;</span> + throwable.getMessage());</span><br><span class="line">            throwable.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        log.info(methodName , <span class="string">&quot;&#123;&#125; 方法执行结束&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 前置通知: 在目标方法(div)运行之前运行 (<span class="doctag">@Before</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Before(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logStart</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;before: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 后置通知：在目标方法(div)运行结束之后运行,无论正常或异常结束 (<span class="doctag">@After</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@After(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logAfter</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;after: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回通知：在目标方法(div)正常返回之后运行 (<span class="doctag">@AfterReturning</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;aspect()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logAfterReturn</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;afterReturn: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 异常通知：在目标方法(div)出现异常后运行(<span class="doctag">@AfterThrowing</span>)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AfterThrowing(pointcut = &quot;aspect()&quot;, throwing = &quot;ex&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logException</span><span class="params">(JoinPoint joinPoint, Exception ex)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;afterThrow: &#123;&#125;&quot;</span>, joinPoint);</span><br><span class="line">        log.info(<span class="string">&quot;message: &#123;&#125;&quot;</span>, ex.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="4、编写切点方法"><a href="#4、编写切点方法" class="headerlink" title="4、编写切点方法"></a>4、编写切点方法</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Log4j2</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BaseController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/aop&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String, Object&gt; <span class="title function_">aop1</span><span class="params">()</span> &#123;</span><br><span class="line">        Map&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">16</span>);</span><br><span class="line">        map.put(<span class="string">&quot;nowTime&quot;</span>, LocalDateTime.now().format(DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>)));</span><br><span class="line">        log.info(<span class="string">&quot;map = &quot;</span> + map);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@MyLog</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/aop2&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String, Object&gt; <span class="title function_">aop2</span><span class="params">()</span> &#123;</span><br><span class="line">        Map&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">16</span>);</span><br><span class="line">        map.put(<span class="string">&quot;nowTime&quot;</span>, LocalDateTime.now().format(DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>)));</span><br><span class="line">        log.info(<span class="string">&quot;map = &quot;</span> + map);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="5、结果"><a href="#5、结果" class="headerlink" title="5、结果"></a>5、结果</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">进入[aop2]方法,参数为:</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">1</span>] com.ase.aop.config.LogAspect             : before: execution(Map com.ase.aop.controller.BaseController.aop2())</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">1</span>] com.ase.aop.controller.BaseController    : map = &#123;nowTime=<span class="number">2021</span>-<span class="number">07</span>-<span class="number">25</span> <span class="number">00</span>:<span class="number">04</span>:<span class="number">02</span>&#125;</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">1</span>] com.ase.aop.config.LogAspect             : afterReturn: execution(Map com.ase.aop.controller.BaseController.aop2()) </span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">1</span>] com.ase.aop.config.LogAspect             : after: execution(Map com.ase.aop.controller.BaseController.aop2())</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">1</span>] com.ase.aop.config.LogAspect             : around <span class="title function_">execution</span><span class="params">(Map com.ase.aop.controller.BaseController.aop2()</span>)	Use time : <span class="number">17</span> ms!</span><br><span class="line">[nio-<span class="number">8080</span>-exec-<span class="number">1</span>] com.ase.aop.config.LogAspect             : aop2方法执行结束</span><br></pre></td></tr></table></figure>

<h5 id="注意-5"><a href="#注意-5" class="headerlink" title="注意"></a>注意</h5><p><strong>在编写的切点方法中只有标注了自定义的注解 MyLog 的 aop2 切点方法才会被 Aop 切面所处理</strong></p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://curry3035.gitee.io">PaulGeorge</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://curry3035.gitee.io/posts/16824/">https://curry3035.gitee.io/posts/16824/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://curry3035.gitee.io" target="_blank">Celts</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Spring/">Spring</a></div><div class="post_share"><div class="social-share" data-image="/img/avatar.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/posts/65011/" title="每日一面--SpringBoot启动原理"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">每日一面--SpringBoot启动原理</div></div></a></div><div class="next-post pull-right"><a href="/posts/4046/" title="每日一面--TCP 3次挥手与4次握手"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">每日一面--TCP 3次挥手与4次握手</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/posts/11315/" title="实践一下--Spring Security"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-01</div><div class="title">实践一下--Spring Security</div></div></a></div><div><a href="/posts/52633/" title="每日一面--Spring 循环依赖以及三级缓存"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-07-21</div><div class="title">每日一面--Spring 循环依赖以及三级缓存</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/img/avatar.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">PaulGeorge</div><div class="author-info__description">很多事情就像是旅行一样，当你决定要出发的时候，最困难的那部分其实就已经完成了</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">97</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">64</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">25</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/PaulGeorge123"><i class="fab fa-github"></i><span>GitHub URL</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/PaulGeorge123" target="_blank" title="Github"><i class="fab fa-github"></i></a></div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring"><span class="toc-number">1.</span> <span class="toc-text">Spring</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Spring-%E6%A0%B8%E5%BF%83"><span class="toc-number">1.1.</span> <span class="toc-text">Spring 核心</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-%E7%9A%84%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84"><span class="toc-number">1.1.1.</span> <span class="toc-text">Spring 的整体架构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-%E5%BA%94%E7%94%A8%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="toc-number">1.1.2.</span> <span class="toc-text">Spring 应用的设计模式</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Spring-%E4%B8%89%E5%B1%82%E6%9E%B6%E6%9E%84"><span class="toc-number">1.2.</span> <span class="toc-text">Spring 三层架构</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F"><span class="toc-number">1.2.0.1.</span> <span class="toc-text">注意</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%B8%E5%BF%83%E5%AE%B9%E5%99%A8"><span class="toc-number">1.2.1.</span> <span class="toc-text">核心容器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E8%AE%BF%E9%97%AE-%E9%9B%86%E6%88%90"><span class="toc-number">1.2.2.</span> <span class="toc-text">数据访问&#x2F;集成</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Web"><span class="toc-number">1.2.3.</span> <span class="toc-text">Web</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Spring-%E6%A1%86%E6%9E%B6%E7%9A%84%E4%BC%98%E7%82%B9"><span class="toc-number">1.3.</span> <span class="toc-text">Spring 框架的优点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Bean-%E7%9A%84%E6%A6%82%E5%BF%B5"><span class="toc-number">1.4.</span> <span class="toc-text">Bean 的概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#IOC-%E4%B8%8E-DI"><span class="toc-number">1.5.</span> <span class="toc-text">IOC 与 DI</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#IOC-%E5%AE%9A%E4%B9%89"><span class="toc-number">1.5.1.</span> <span class="toc-text">IOC 定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A7%A3%E9%87%8A"><span class="toc-number">1.5.2.</span> <span class="toc-text">解释</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A5%BD%E5%A4%84"><span class="toc-number">1.5.3.</span> <span class="toc-text">好处</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F-1"><span class="toc-number">1.5.4.</span> <span class="toc-text">注意</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-%E7%9A%84%E4%B8%A4%E7%A7%8D-IoC-%E5%AE%B9%E5%99%A8"><span class="toc-number">1.5.5.</span> <span class="toc-text">Spring 的两种 IoC 容器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#IoC-%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F"><span class="toc-number">1.5.6.</span> <span class="toc-text">IoC 的两种实现方式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DI-%E5%AE%9A%E4%B9%89"><span class="toc-number">1.5.7.</span> <span class="toc-text">DI 定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F"><span class="toc-number">1.5.8.</span> <span class="toc-text">实现方式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F-2"><span class="toc-number">1.5.9.</span> <span class="toc-text">注意</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AOP"><span class="toc-number">1.6.</span> <span class="toc-text">AOP</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E7%9A%84%E5%AE%9A%E4%B9%89"><span class="toc-number">1.6.1.</span> <span class="toc-text">AOP 的定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E7%9A%84%E4%BC%98%E5%8A%BF"><span class="toc-number">1.6.2.</span> <span class="toc-text">AOP 的优势</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E4%BD%93%E7%B3%BB"><span class="toc-number">1.6.3.</span> <span class="toc-text">AOP 体系</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E7%9A%84%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-number">1.6.4.</span> <span class="toc-text">AOP 的使用场景</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E7%9A%84%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86"><span class="toc-number">1.6.5.</span> <span class="toc-text">AOP 的底层原理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E7%9A%84%E9%87%8D%E8%A6%81%E5%90%8D%E8%AF%8D"><span class="toc-number">1.6.6.</span> <span class="toc-text">AOP 的重要名词</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E9%80%9A%E7%9F%A5%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F"><span class="toc-number">1.6.7.</span> <span class="toc-text">AOP 通知执行顺序</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F"><span class="toc-number">1.6.7.1.</span> <span class="toc-text">执行顺序</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F-3"><span class="toc-number">1.6.7.2.</span> <span class="toc-text">注意</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#1%E3%80%81%E5%8D%95%E4%B8%AA%E5%88%87%E9%9D%A2%E7%B1%BB"><span class="toc-number">1.6.7.3.</span> <span class="toc-text">1、单个切面类</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2%E3%80%81%E5%A4%9A%E4%B8%AA%E5%88%87%E9%9D%A2"><span class="toc-number">1.6.7.4.</span> <span class="toc-text">2、多个切面</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3%E3%80%81-Order%E6%B3%A8%E8%A7%A3%E6%8C%87%E5%AE%9A%E5%85%88%E5%90%8E%E9%A1%BA%E5%BA%8F"><span class="toc-number">1.6.7.5.</span> <span class="toc-text">3、@Order注解指定先后顺序</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP-%E5%AE%9E%E8%B7%B5"><span class="toc-number">1.6.8.</span> <span class="toc-text">AOP 实践</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1%E3%80%81%E5%BC%95%E5%85%A5-Aop-%E7%9A%84%E4%BE%9D%E8%B5%96"><span class="toc-number">1.6.8.1.</span> <span class="toc-text">1、引入 Aop 的依赖</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2%E3%80%81%E7%BC%96%E5%86%99-Aop-%E5%88%87%E9%9D%A2%E7%B1%BB"><span class="toc-number">1.6.8.2.</span> <span class="toc-text">2、编写 Aop 切面类</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3%E3%80%81%E7%BC%96%E5%86%99%E5%88%87%E7%82%B9%E6%96%B9%E6%B3%95"><span class="toc-number">1.6.8.3.</span> <span class="toc-text">3、编写切点方法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#4%E3%80%81%E7%BB%93%E6%9E%9C"><span class="toc-number">1.6.8.4.</span> <span class="toc-text">4、结果</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F-4"><span class="toc-number">1.6.8.5.</span> <span class="toc-text">注意</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8-annotation%EF%BC%88%E6%B3%A8%E8%A7%A3%EF%BC%89"><span class="toc-number">1.6.9.</span> <span class="toc-text">使用 annotation（注解）</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#annotation-%E6%B3%A8%E8%A7%A3%E5%B1%9E%E6%80%A7"><span class="toc-number">1.6.9.1.</span> <span class="toc-text">annotation 注解属性</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#annotation-%E6%B3%A8%E8%A7%A3%E5%B1%9E%E6%80%A7%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.6.9.2.</span> <span class="toc-text">annotation 注解属性类型</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#annotation-%E5%8F%8D%E5%B0%84%E8%8E%B7%E5%8F%96%E6%B3%A8%E8%A7%A3"><span class="toc-number">1.6.9.3.</span> <span class="toc-text">annotation 反射获取注解</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Log-%E6%97%A5%E5%BF%97%E5%AE%9E%E8%B7%B5"><span class="toc-number">1.6.10.</span> <span class="toc-text">Log 日志实践</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1%E3%80%81%E5%BC%95%E5%85%A5-Aop-%E7%9A%84%E4%BE%9D%E8%B5%96-1"><span class="toc-number">1.6.10.1.</span> <span class="toc-text">1、引入 Aop 的依赖</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2%E3%80%81%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3"><span class="toc-number">1.6.10.2.</span> <span class="toc-text">2、自定义注解</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3%E3%80%81%E7%BC%96%E5%86%99-Aop-%E5%88%87%E9%9D%A2%E7%B1%BB"><span class="toc-number">1.6.10.3.</span> <span class="toc-text">3、编写 Aop 切面类</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#4%E3%80%81%E7%BC%96%E5%86%99%E5%88%87%E7%82%B9%E6%96%B9%E6%B3%95"><span class="toc-number">1.6.10.4.</span> <span class="toc-text">4、编写切点方法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#5%E3%80%81%E7%BB%93%E6%9E%9C"><span class="toc-number">1.6.10.5.</span> <span class="toc-text">5、结果</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F-5"><span class="toc-number">1.6.10.6.</span> <span class="toc-text">注意</span></a></li></ol></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/47231/" title="POI读取Excel问题">POI读取Excel问题</a><time datetime="2023-04-11T01:00:00.000Z" title="发表于 2023-04-11 09:00:00">2023-04-11</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/8422/" title="Excel大文件的上传">Excel大文件的上传</a><time datetime="2023-04-10T01:00:00.000Z" title="发表于 2023-04-10 09:00:00">2023-04-10</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/55119/" title="每日一面--Files工具类">每日一面--Files工具类</a><time datetime="2023-01-01T01:00:00.000Z" title="发表于 2023-01-01 09:00:00">2023-01-01</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/34600/" title="面试一下--JUC入门">面试一下--JUC入门</a><time datetime="2022-09-10T01:00:00.000Z" title="发表于 2022-09-10 09:00:00">2022-09-10</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/16284/" title="每日一面--字符流和字节流">每日一面--字符流和字节流</a><time datetime="2022-07-01T01:00:00.000Z" title="发表于 2022-07-01 09:00:00">2022-07-01</time></div></div></div></div></div></div></main><footer id="footer" style="background-image: url('https://gcore.jsdelivr.net/gh/PaulGeorge123/cloudimg@img/mig2023/background05.jpg')"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By PaulGeorge</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><div class="js-pjax"></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div><script src="/js/search/local-search.js"></script></div></body></html>